#include <core/types.h>
#include <core/io.h>
#include <core/init.h>
#include <core/list.h>
#include <core/timer.h>
#include <net/net.h>
#include <net/mii.h>
#include <autoconf.h>
#include <arch/arm.h>
#include "eth_at91.h"

#define EMAC_PID  21

#define BUFF_LEN  128
#define BUFF_NUM  8

#define EMAC_SOF  (1 << 14)
#define EMAC_EOF  (1 << 15)


struct EmacBuffDesc
{
	UINT32 dwAddr;
	UINT32 dwStat;
};

static UINT32 g_dwRxRing, g_dwTxRing;
static struct EmacBuffDesc *g_pStartDesc;

static void EmacRecv(void);

static int EmacIsr(void *pDev)
{
	UINT32 dwStat;

	dwStat = EmacRead(EMAC_ISR);

	if (dwStat & 0x2)
		EmacRecv();

	return 0;
}

static int EmacSendPacket(struct NetDevice *pNetDev, struct SocketBuffer *pSockBuff)
{
	static int i = 0;
	volatile UINT32 val;
	volatile struct EmacBuffDesc *pDesc;
	ULONG ulFlag;

	LockIrqPsr(ulFlag);

	pDesc = (struct EmacBuffDesc *)g_dwTxRing + i;

	pDesc->dwAddr = (UINT32)pSockBuff->pSkbData;;
	pDesc->dwStat = (1 << 15) | pSockBuff->wSkbSize;
	if (i == BUFF_NUM - 1)
		pDesc->dwStat |= 1 << 30;

	val = EmacRead(EMAC_NCR);
	EmacWrite(EMAC_NCR, val | (1 << 9));

	i++;
	if (i >= BUFF_NUM)
		i = 0;

#if 1
	while (1)
	{
		val = EmacRead(EMAC_TSR);
		if (val & (1 << 5))
			break;
	}

	EmacWrite(EMAC_TSR, val);
#endif

	UnlockIrqPsr(ulFlag);
}

#if 0
static void DumpBuff(const struct EmacBuffDesc* pDesc)
{
	int j;
	const BYTE *pBuff;

	pBuff = (const BYTE *)(pDesc->dwAddr & ~0x3);
	
	printf("Buf @ 0x%08x: 0x%08x\n", pDesc, pDesc->dwStat);

	for (j = 0; j < BUFF_LEN; j++)
		printf("%02x ", pBuff[j]);

	puts("\n");
}

static void DumpQueue(void)
{
	int i;


	for (i = 0; i < BUFF_NUM; i++)
	{
		DumpBuff((struct EmacBuffDesc*)g_dwRxRing + i);
	}
}
#endif

static void EmacRecv(void)
{
	static int i = 0, j;
	ULONG nLen, nCount = 0;
	struct SocketBuffer *pSockBuff;
	struct EmacBuffDesc *pEndDesc, *pCurDesc;
	UINT8 *pBuff;


	pCurDesc = (struct EmacBuffDesc *)EmacRead(EMAC_RBQP);

	// printf("current = 0x%08x\n", pCurDesc);
	// DumpQueue();

	if (pCurDesc == (struct EmacBuffDesc *)g_dwRxRing)
		 pEndDesc = (struct EmacBuffDesc *)g_dwRxRing + BUFF_NUM - 1;
	else
		pEndDesc = pCurDesc - 1;
		
	if (!(g_pStartDesc->dwStat & EMAC_SOF) || !(pEndDesc->dwStat & EMAC_EOF))
		printf("bug!\n");

	// if nLen ...
	nLen = pEndDesc->dwStat & 0xfff;

	pSockBuff = GkSockBuffAlloc(0, nLen);

	while (g_pStartDesc != pCurDesc)
	{
		UINT32 nBufLen;

		nBufLen = nLen - nCount;
		if (nBufLen > BUFF_LEN)
			nBufLen = BUFF_LEN;

		memcpy(pSockBuff->pSkbData + nCount, (BYTE*)(g_pStartDesc->dwAddr & ~0x3), nBufLen);
		g_pStartDesc->dwAddr &= ~1;

		nCount += nBufLen;

		g_pStartDesc++;
		if (g_pStartDesc == (struct EmacBuffDesc *)g_dwRxRing + BUFF_NUM)
			g_pStartDesc = (struct EmacBuffDesc *)g_dwRxRing;
	}

	GkNetRxDcp(pSockBuff);
}


static UINT16 EmacMdioRead(UINT8 nReg)
{
	UINT32 dwNetCtrl;
	UINT32 dwFrame;


	dwFrame = (1 << 30) | (2 << 28) | (0 << 23) | (nReg << 18) | (2 << 16);
	EmacWrite(EMAC_MAN, dwFrame);

	while (!(EmacRead(EMAC_NSR) & EMAC_IDLE));

	dwFrame = EmacRead(EMAC_MAN);

	return (UINT16)(dwFrame & 0xffff);
}


static int EmacMdioWrite(int nReg, UINT16 hVal)
{
	EmacWrite(EMAC_MAN, (1 << 30) | (1 << 28) | (0 << 23) | (nReg << 18) | (2 << 16) | hVal);

	while (!(EmacRead(EMAC_NSR) & EMAC_IDLE));

	return 0;
}

static void EmacInitRingBuff(void)
{
	int i;
	UINT32 dwBuffBase;
	struct EmacBuffDesc *pDesc;

	g_pStartDesc = pDesc = (struct EmacBuffDesc *)malloc(8 * BUFF_NUM);
	dwBuffBase = (UINT32)malloc(BUFF_LEN * BUFF_NUM);
	
	g_dwRxRing = (UINT32)pDesc;

	for (i = 0; i < BUFF_NUM; i++)
	{
		pDesc->dwAddr = dwBuffBase;
		pDesc->dwStat = 0;

		pDesc++;
		dwBuffBase += BUFF_LEN;
	}
	(pDesc - 1)->dwAddr |= 0x2;


	pDesc = (struct EmacBuffDesc *)malloc(8 * BUFF_NUM);
	
	g_dwTxRing = (UINT32)pDesc;

	for (i = 0; i < BUFF_NUM; i++)
	{
		pDesc->dwAddr = 0;
		pDesc->dwStat = 1 << 31;

		pDesc++;
	}
	(pDesc - 1)->dwAddr |= (1 << 30);

	printf("rx buff = 0x%08x, tx buff = 0x%08x\n", g_dwRxRing, g_dwTxRing);

	EmacWrite(EMAC_RBQP, g_dwRxRing);
	EmacWrite(EMAC_TBQP, g_dwTxRing);
}

static void EmacInit(void)
{
	UINT32 dwConf;

	EmacWrite(EMAC_NCR, 0);

	EmacWrite(EMAC_TSR, ~0UL);
	EmacWrite(EMAC_RSR, ~0UL);

	EmacWrite(EMAC_IDR, ~0UL);
	EmacRead(EMAC_ISR);

	dwConf = EmacRead(EMAC_NCFGR);
	dwConf &= (3 << 10);
	dwConf |= (1 << 17);
	dwConf |= (1 << 13);
	dwConf |= 3;
	EmacWrite(EMAC_NCFGR, dwConf);

	EmacWrite(EMAC_NCR, 0x3c);
	
	EmacWrite(EMAC_USRIO, 0x3);
}

static void PhyReset(void)
{
	UINT16 hVal;
	int nTimeOut = 100;

	EmacMdioWrite(MII_REG_BMC, MII_PHY_RESET);

	while (nTimeOut > 0)
	{
		hVal = EmacMdioRead(MII_REG_BMC);
		if (!(hVal & MII_PHY_RESET))
			return;

		udelay(10);
		nTimeOut--;
	}

	printf("PHY reset failed!\n");
}

static int EmacSetMacAddr(struct NetDevice *pNetDev, const BYTE *pbMacAddr)
{
	memcpy(pNetDev->vbMacAddr, pbMacAddr, MAC_ADDR_LEN);

	EmacWrite(EMAC_SA1B, *(UINT32 *)pNetDev->vbMacAddr);
	EmacWrite(EMAC_SA1T, *(UINT16 *)(pNetDev->vbMacAddr + 4));

	return 0;
}

static __INIT__ int Eth9263Probe(void)
{
	int i;
	UINT16 hPhyID, hSpeed, hMode;
	UINT32 dwStat;
	struct NetDevice *pNetDev;

	printf("%s(): %s\n", __FUNCTION__, __TIME__);

	pNetDev = GkNetDevNew();

	WriteLong(1 << EMAC_PID, AT91SAM926X_PA_PMC + PMC_PCER);

	EmacInitRingBuff();

	EmacInit();

	PhyReset();

	while (1) // fixme
	{
		hMode = EmacMdioRead(MII_REG_BMS);
		if (hMode & 0x24)
			break;
	}

	if(hMode & 0x20)
		printf("Auto negotiation ok!\n");
	if(hMode & 0x4)
		printf("Link ok!\n");

	hSpeed = EmacMdioRead(MII_REG_STAT);

	printf("hSpeed = 0x%04x\n", hSpeed);

	pNetDev->SendPacket = EmacSendPacket;
	pNetDev->SetMacAddr = EmacSetMacAddr;

	RegisterIsr(EMAC_PID, EmacIsr, NULL);

	EmacWrite(EMAC_IER, 0x3cf7);

	return GkNetDevRegister(pNetDev);
}

DRIVER_INIT(Eth9263Probe);

